﻿Imports System
Imports System.CodeDom
Imports System.Collections.Generic
Imports System.Diagnostics.CodeAnalysis
Imports System.Globalization
Imports System.Reflection
Imports System.Runtime.InteropServices
Imports System.Workflow.ComponentModel
Imports System.Workflow.ComponentModel.Compiler

Namespace System.Workflow.Activities.Rules
    Friend Class Executor
        ' Methods
        Friend Shared Function AdjustType(ByVal operandType As Type, ByVal operandValue As Object, ByVal toType As Type) As Object
            Dim obj2 As Object
            Dim error As ValidationError
            Dim obj3 As Object
            If (operandType Is toType) Then
                Return operandValue
            End If
            If Executor.AdjustValueStandard(operandType, operandValue, toType, obj2) Then
                Return obj2
            End If
            Dim info As MethodInfo = RuleValidation.FindImplicitConversion(operandType, toType, [error])
            If (info Is Nothing) Then
                If (Not [error] Is Nothing) Then
                    Throw New RuleEvaluationException([error].ErrorText)
                End If
                Throw New RuleEvaluationException(String.Format(CultureInfo.CurrentCulture, Messages.CastIncompatibleTypes, New Object() { RuleDecompiler.DecompileType(operandType), RuleDecompiler.DecompileType(toType) }))
            End If
            Dim parameterType As Type = info.GetParameters(0).ParameterType
            Dim returnType As Type = info.ReturnType
            If Executor.AdjustValueStandard(operandType, operandValue, parameterType, obj3) Then
                Dim obj5 As Object
                Dim obj4 As Object = info.Invoke(Nothing, New Object() { obj3 })
                If Executor.AdjustValueStandard(returnType, obj4, toType, obj5) Then
                    Return obj5
                End If
            End If
            Throw New RuleEvaluationException(String.Format(CultureInfo.CurrentCulture, Messages.CastIncompatibleTypes, New Object() { RuleDecompiler.DecompileType(operandType), RuleDecompiler.DecompileType(toType) }))
        End Function

        Friend Shared Function AdjustTypeWithCast(ByVal operandType As Type, ByVal operandValue As Object, ByVal toType As Type) As Object
            Dim obj2 As Object
            Dim error As ValidationError
            Dim obj3 As Object
            If (operandType Is toType) Then
                Return operandValue
            End If
            If Executor.AdjustValueStandard(operandType, operandValue, toType, obj2) Then
                Return obj2
            End If
            Dim info As MethodInfo = RuleValidation.FindExplicitConversion(operandType, toType, [error])
            If (info Is Nothing) Then
                If (Not [error] Is Nothing) Then
                    Throw New RuleEvaluationException([error].ErrorText)
                End If
                Throw New RuleEvaluationException(String.Format(CultureInfo.CurrentCulture, Messages.CastIncompatibleTypes, New Object() { RuleDecompiler.DecompileType(operandType), RuleDecompiler.DecompileType(toType) }))
            End If
            Dim parameterType As Type = info.GetParameters(0).ParameterType
            Dim returnType As Type = info.ReturnType
            If Executor.AdjustValueStandard(operandType, operandValue, parameterType, obj3) Then
                Dim obj5 As Object
                Dim obj4 As Object = info.Invoke(Nothing, New Object() { obj3 })
                If Executor.AdjustValueStandard(returnType, obj4, toType, obj5) Then
                    Return obj5
                End If
            End If
            Throw New RuleEvaluationException(String.Format(CultureInfo.CurrentCulture, Messages.CastIncompatibleTypes, New Object() { RuleDecompiler.DecompileType(operandType), RuleDecompiler.DecompileType(toType) }))
        End Function

        <SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")> _
        Private Shared Function AdjustValueStandard(ByVal operandType As Type, ByVal operandValue As Object, ByVal toType As Type, <Out> ByRef converted As Object) As Boolean
            converted = operandValue
            If (operandValue Is Nothing) Then
                Dim error As ValidationError
                If Not toType.IsValueType Then
                    Return True
                End If
                If Not ConditionHelper.IsNullableValueType(toType) Then
                    Throw New InvalidCastException(String.Format(CultureInfo.CurrentCulture, Messages.CannotCastNullToValueType, New Object() { RuleDecompiler.DecompileType(toType) }))
                End If
                converted = Activator.CreateInstance(toType)
                Return RuleValidation.StandardImplicitConversion(operandType, toType, Nothing, [error])
            End If
            Dim c As Type = operandValue.GetType
            If (c Is toType) Then
                Return True
            End If
            If toType.IsAssignableFrom(c) Then
                Return True
            End If
            If (c.IsValueType AndAlso toType.IsValueType) Then
                If c.IsEnum Then
                    c = Enum.GetUnderlyingType(c)
                    operandValue = ArithmeticLiteral.MakeLiteral(c, operandValue).Value
                End If
                Dim flag As Boolean = ConditionHelper.IsNullableValueType(toType)
                Dim enumType As Type = If(flag, Nullable.GetUnderlyingType(toType), toType)
                If enumType.IsEnum Then
                    Dim obj2 As Object
                    Dim underlyingType As Type = Enum.GetUnderlyingType(enumType)
                    If Executor.AdjustValueStandard(c, operandValue, underlyingType, obj2) Then
                        converted = Enum.ToObject(enumType, obj2)
                        If flag Then
                            converted = Activator.CreateInstance(toType, New Object() { converted })
                        End If
                        Return True
                    End If
                ElseIf (enumType.IsPrimitive OrElse (enumType Is GetType(Decimal))) Then
                    If (c Is GetType(Char)) Then
                        Dim ch As Char = DirectCast(operandValue, Char)
                        If (enumType Is GetType(Single)) Then
                            converted = CSng(ch)
                        ElseIf (enumType Is GetType(Double)) Then
                            converted = CDbl(ch)
                        ElseIf (enumType Is GetType(Decimal)) Then
                            converted = ch
                        Else
                            converted = DirectCast(ch, IConvertible).ToType(enumType, CultureInfo.CurrentCulture)
                        End If
                        If flag Then
                            converted = Activator.CreateInstance(toType, New Object() { converted })
                        End If
                        Return True
                    End If
                    If (c Is GetType(Single)) Then
                        Dim num As Single = CSng(operandValue)
                        If (enumType Is GetType(Char)) Then
                            converted = DirectCast([CUShort](num), Char)
                        Else
                            converted = DirectCast(num, IConvertible).ToType(enumType, CultureInfo.CurrentCulture)
                        End If
                        If flag Then
                            converted = Activator.CreateInstance(toType, New Object() { converted })
                        End If
                        Return True
                    End If
                    If (c Is GetType(Double)) Then
                        Dim num2 As Double = CDbl(operandValue)
                        If (enumType Is GetType(Char)) Then
                            converted = DirectCast([CUShort](num2), Char)
                        Else
                            converted = DirectCast(num2, IConvertible).ToType(enumType, CultureInfo.CurrentCulture)
                        End If
                        If flag Then
                            converted = Activator.CreateInstance(toType, New Object() { converted })
                        End If
                        Return True
                    End If
                    If (c Is GetType(Decimal)) Then
                        Dim num3 As Decimal = CDec(operandValue)
                        If (enumType Is GetType(Char)) Then
                            converted = DirectCast(num3, Char)
                        Else
                            converted = DirectCast(num3, IConvertible).ToType(enumType, CultureInfo.CurrentCulture)
                        End If
                        If flag Then
                            converted = Activator.CreateInstance(toType, New Object() { converted })
                        End If
                        Return True
                    End If
                    Dim convertible As IConvertible = [TryCast](operandValue,IConvertible)
                    If (Not convertible Is Nothing) Then
                        Try 
                            converted = convertible.ToType(enumType, CultureInfo.CurrentCulture)
                            If flag Then
                                converted = Activator.CreateInstance(toType, New Object() { converted })
                            End If
                            Return True
                        Catch exception1 As InvalidCastException
                            Return False
                        End Try
                    End If
                End If
            End If
            Return False
        End Function

        Private Shared Function AnalyzeRule(ByVal behavior As RuleChainingBehavior, ByVal rule As Rule, ByVal validator As RuleValidation, ByVal tracer As Tracer) As RuleSymbolInfo
            Dim info As New RuleSymbolInfo
            If (Not rule.Condition Is Nothing) Then
                info.conditionDependencies = rule.Condition.GetDependencies(validator)
                If ((Not info.conditionDependencies Is Nothing) AndAlso (Not tracer Is Nothing)) Then
                    tracer.TraceConditionSymbols(rule.Name, info.conditionDependencies)
                End If
            End If
            If (Not rule.thenActions Is Nothing) Then
                info.thenSideEffects = Executor.GetActionSideEffects(behavior, rule.thenActions, validator)
                If ((Not info.thenSideEffects Is Nothing) AndAlso (Not tracer Is Nothing)) Then
                    tracer.TraceThenSymbols(rule.Name, info.thenSideEffects)
                End If
            End If
            If (Not rule.elseActions Is Nothing) Then
                info.elseSideEffects = Executor.GetActionSideEffects(behavior, rule.elseActions, validator)
                If ((Not info.elseSideEffects Is Nothing) AndAlso (Not tracer Is Nothing)) Then
                    tracer.TraceElseSymbols(rule.Name, info.elseSideEffects)
                End If
            End If
            Return info
        End Function

        Private Shared Sub AnalyzeRules(ByVal behavior As RuleChainingBehavior, ByVal ruleStates As List([Of] RuleState), ByVal validation As RuleValidation, ByVal tracer As Tracer)
            Dim count As Integer = ruleStates.Count
            If (behavior <> RuleChainingBehavior.None) Then
                Dim ruleSymbols As RuleSymbolInfo() = New RuleSymbolInfo(count  - 1) {}
                Dim index As Integer = 0
                Do While (index < count)
                    ruleSymbols(index) = Executor.AnalyzeRule(behavior, ruleStates.Item(index).Rule, validation, tracer)
                    index += 1
                Loop
                index
                For index = 0 To count - 1
                    Dim state As RuleState = ruleStates.Item(index)
                    If (Not ruleSymbols(index).thenSideEffects Is Nothing) Then
                        state.ThenActionsActiveRules = Executor.AnalyzeSideEffects(ruleSymbols(index).thenSideEffects, ruleSymbols)
                        If ((Not state.ThenActionsActiveRules Is Nothing) AndAlso (Not tracer Is Nothing)) Then
                            tracer.TraceThenTriggers(state.Rule.Name, state.ThenActionsActiveRules, ruleStates)
                        End If
                    End If
                    If (Not ruleSymbols(index).elseSideEffects Is Nothing) Then
                        state.ElseActionsActiveRules = Executor.AnalyzeSideEffects(ruleSymbols(index).elseSideEffects, ruleSymbols)
                        If ((Not state.ElseActionsActiveRules Is Nothing) AndAlso (Not tracer Is Nothing)) Then
                            tracer.TraceElseTriggers(state.Rule.Name, state.ElseActionsActiveRules, ruleStates)
                        End If
                    End If
                Next index
            End If
        End Sub

        Private Shared Function AnalyzeSideEffects(ByVal sideEffects As ICollection([Of] String), ByVal ruleSymbols As RuleSymbolInfo()) As ICollection([Of] Integer)
            Dim dictionary As New Dictionary([Of] Integer, Object)
            Dim i As Integer
            For i = 0 To ruleSymbols.Length - 1
                Dim conditionDependencies As ICollection([Of] String) = ruleSymbols(i).conditionDependencies
                If (Not conditionDependencies Is Nothing) Then
                    Dim str As String
                    For Each str In sideEffects
                        Dim flag As Boolean = False
                        If str.EndsWith("*", StringComparison.Ordinal) Then
                            Dim str2 As String
                            For Each str2 In conditionDependencies
                                If str2.EndsWith("*", StringComparison.Ordinal) Then
                                    Dim str5 As String
                                    Dim str6 As String
                                    Dim str3 As String = str2.Substring(0, (str2.Length - 2))
                                    Dim str4 As String = str.Substring(0, (str.Length - 1))
                                    If (str3.Length < str4.Length) Then
                                        str5 = str3
                                        str6 = str4
                                    Else
                                        str5 = str4
                                        str6 = str3
                                    End If
                                    If Not str6.StartsWith(str5, StringComparison.Ordinal) Then
                                        [Continue] For
                                    End If
                                    flag = True
                                    Exit For
                                End If
                                Dim str7 As String = str.Substring(0, (str.Length - 1))
                                Dim str8 As String = str2
                                If str8.EndsWith("/", StringComparison.Ordinal) Then
                                    str8 = str8.Substring(0, (str8.Length - 1))
                                End If
                                If str8.StartsWith(str7, StringComparison.Ordinal) Then
                                    flag = True
                                    Exit For
                                End If
                            Next
                        Else
                            Dim str9 As String
                            For Each str9 In conditionDependencies
                                If str9.EndsWith("*", StringComparison.Ordinal) Then
                                    Dim str11 As String
                                    Dim str12 As String
                                    Dim str10 As String = str9.Substring(0, (str9.Length - 2))
                                    If (str10.Length < str.Length) Then
                                        str11 = str10
                                        str12 = str
                                    Else
                                        str11 = str
                                        str12 = str10
                                    End If
                                    If Not str12.StartsWith(str11, StringComparison.Ordinal) Then
                                        [Continue] For
                                    End If
                                    flag = True
                                    Exit For
                                End If
                                If str9.StartsWith(str, StringComparison.Ordinal) Then
                                    flag = True
                                    Exit For
                                End If
                            Next
                        End If
                        If flag Then
                            dictionary.Item(i) = Nothing
                            Exit For
                        End If
                    Next
                End If
            Next i
            Return dictionary.Keys
        End Function

        Friend Shared Function EvaluateBool(ByVal expression As CodeExpression, ByVal context As RuleExecution) As Boolean
            Dim operandValue As Object = RuleExpressionWalker.Evaluate(context, expression).Value
            If TypeOf operandValue Is Boolean Then
                Return CBool(operandValue)
            End If
            Dim expressionType As Type = context.Validation.ExpressionInfo(expression).ExpressionType
            If (expressionType Is Nothing) Then
                Dim exception As New InvalidOperationException(Messages.ConditionMustBeBoolean)
                exception.Data.Item("ErrorObject") = expression
                Throw exception
            End If
            Return CBool(Executor.AdjustType(expressionType, operandValue, GetType(Boolean)))
        End Function

        Friend Shared Sub ExecuteRuleSet(ByVal orderedRules As IList([Of] RuleState), ByVal ruleExecution As RuleExecution, ByVal tracer As Tracer, ByVal trackingKey As String)
            Dim numArray As Long() = New Long(orderedRules.Count  - 1) {}
            Dim flagArray As Boolean() = New Boolean(orderedRules.Count  - 1) {}
            ruleExecution.Halted = False
            Dim activityExecutionContext As ActivityExecutionContext = ruleExecution.ActivityExecutionContext
            Dim index As Integer = 0
            Do While (index < orderedRules.Count)
                Dim state As RuleState = orderedRules.Item(index)
                If Not flagArray(index) Then
                    If (Not tracer Is Nothing) Then
                        tracer.StartRule(state.Rule.Name)
                    End If
                    flagArray(index) = True
                    Dim result As Boolean = state.Rule.Condition.Evaluate(ruleExecution)
                    If (Not tracer Is Nothing) Then
                        tracer.RuleResult(state.Rule.Name, result)
                    End If
                    If ((Not activityExecutionContext Is Nothing) AndAlso (Not state.Rule.Name Is Nothing)) Then
                        activityExecutionContext.TrackData(trackingKey, New RuleActionTrackingEvent(state.Rule.Name, result))
                    End If
                    Dim is2 As ICollection([Of] RuleAction) = If(result, state.Rule.thenActions, state.Rule.elseActions)
                    Dim is3 As ICollection([Of] Integer) = If(result, state.ThenActionsActiveRules, state.ElseActionsActiveRules)
                    If ((Not is2 Is Nothing) AndAlso (is2.Count > 0)) Then
                        numArray(index) = (numArray(index) + 1)
                        Dim name As String = state.Rule.Name
                        If (Not tracer Is Nothing) Then
                            tracer.StartActions(name, result)
                        End If
                        Dim action As RuleAction
                        For Each action In is2
                            action.Execute(ruleExecution)
                            If ruleExecution.Halted Then
                                Exit For
                            End If
                        Next
                        If ruleExecution.Halted Then
                            Return
                        End If
                        If (Not is3 Is Nothing) Then
                            Dim num2 As Integer
                            For Each num2 In is3
                                Dim state2 As RuleState = orderedRules.Item(num2)
                                If (flagArray(num2) AndAlso ((numArray(num2) = 0) OrElse (state2.Rule.ReevaluationBehavior = RuleReevaluationBehavior.Always))) Then
                                    If (Not tracer Is Nothing) Then
                                        tracer.TraceUpdate(name, state2.Rule.Name)
                                    End If
                                    flagArray(num2) = False
                                    If (num2 < index) Then
                                        index = num2
                                    End If
                                End If
                            Next
                        End If
                        [Continue] Do
                    End If
                End If
                index += 1
            Loop
        End Sub

        Private Shared Function GetActionSideEffects(ByVal behavior As RuleChainingBehavior, ByVal actions As IList([Of] RuleAction), ByVal validation As RuleValidation) As ICollection([Of] String)
            Dim dictionary As New Dictionary([Of] String, Object)
            Dim action As RuleAction
            For Each action In actions
                If ((behavior = RuleChainingBehavior.Full) OrElse ((behavior = RuleChainingBehavior.UpdateOnly) AndAlso TypeOf action Is RuleUpdateAction)) Then
                    Dim sideEffects As ICollection([Of] String) = action.GetSideEffects(validation)
                    If (Not sideEffects Is Nothing) Then
                        Dim str As String
                        For Each str In sideEffects
                            dictionary.Item(str) = Nothing
                        Next
                        [Continue] For
                    End If
                End If
            Next
            Return dictionary.Keys
        End Function

        Friend Shared Function Preprocess(ByVal behavior As RuleChainingBehavior, ByVal rules As ICollection([Of] Rule), ByVal validation As RuleValidation, ByVal tracer As Tracer) As IList([Of] RuleState)
            Dim ruleStates As New List([Of] RuleState)(rules.Count)
            Dim rule As Rule
            For Each rule In rules
                If rule.Active Then
                    ruleStates.Add(New RuleState(rule))
                End If
            Next
            ruleStates.Sort
            Executor.AnalyzeRules(behavior, ruleStates, validation, tracer)
            Return ruleStates
        End Function


        ' Nested Types
        Private Class RuleSymbolInfo
            ' Fields
            Friend conditionDependencies As ICollection([Of] String)
            Friend elseSideEffects As ICollection([Of] String)
            Friend thenSideEffects As ICollection([Of] String)
        End Class
    End Class
End Namespace

